package com.aliya.glide.module

import android.util.Log
import androidx.annotation.WorkerThread
import com.aliya.glide.App
import com.aliya.glide.builder.InternalCacheDiskCacheCopyFactory
import com.bumptech.glide.load.engine.cache.DiskCache
import com.bumptech.glide.load.model.GlideUrl
import okhttp3.Call
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.internal.notifyAll
import okhttp3.internal.wait
import java.io.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

/**
 * GlideHelper
 *
 * @author a_liYa
 * @date 2022/3/5 20:37.
 *
 */
object GlideHelper {

    private val executorService: ExecutorService = Executors.newCachedThreadPool()
    val okHttpCall: Call.Factory by lazy {
//        val cacheSize = 100L * 1024 * 1024
//        val cache = Cache(
//            File(App.context.cacheDir, "glide_okhttp_disk_cache"),
//            cacheSize
//        )
        OkHttpClient.Builder()
//            .cache(cache)
            .build()
    }
    val diskCacheFactory: DiskCache.Factory by lazy { InternalCacheDiskCacheCopyFactory(App.context) }

    private val diskCache: DiskCache? by lazy { diskCacheFactory.build() }

    @WorkerThread
    fun getResourceInputStream(url: String): InputStream? {
        diskCache?.run {
            val glideUrl = GlideUrl(url)
            get(glideUrl)?.run {
                return FileInputStream(this)
            }
//        val startMs = SystemClock.uptimeMillis()
            val request = Request.Builder()
                .url(url).apply {
                    for ((key, value) in glideUrl.headers) {
                        addHeader(key, value)
                    }
                }.build()

            val response = okHttpCall.newCall(request).execute()
            if (response.isSuccessful) {
                val inputStream = response.body?.byteStream()
                if (inputStream != null) {
                    return object : FilterInputStream(inputStream) {
                        var dataCacheWriter = DataCacheWriter(diskCache!!, glideUrl)

                        override fun read(b: ByteArray?, off: Int, len: Int): Int {
                            val read = super.read(b, off, len)
                            if (read != -1) {
                                dataCacheWriter.output?.write(b, off, read)
                            } else {
                                dataCacheWriter.successful = true
                                dataCacheWriter.notifyCallback()
                            }
                            return read
                        }

                        override fun close() {
                            super.close()
                            dataCacheWriter.notifyCallback()
                        }
                    }
                }
            }
        }
        return null
    }


    class DataCacheWriter(private val diskCache: DiskCache, private var key: GlideUrl) :
        DiskCache.Writer, Runnable {
        companion object {
            const val WRITE_TIME_OUT = 3 * 60 * 1000L // 3min
        }
        init {
            executorService.execute(this)
        }

        var successful = false
        var output: FileOutputStream? = null
            get() {
                if (field == null) {
                    synchronized(this) {
                        wait()
                    }
                }
                return field
            }

        @Synchronized
        override fun write(file: File): Boolean {
            this.output = FileOutputStream(file)
            notifyCallback()
            try {
                (this as Object).wait(WRITE_TIME_OUT)
            } catch (e: Exception) {
                return false
            }
            return successful
        }

        override fun run() {
            diskCache.put(key, this)
        }

        @Synchronized
        fun notifyCallback() {
            try {
                notifyAll()
            } catch (e: Exception) {
                // no-op
            }
        }
    }
}